using Unity.Burst;
using Unity.Burst.Intrinsics;
using Unity.Entities;
using Unity.Transforms;

namespace PathFinding
{
    public partial struct AgentPathSystem : ISystem
    {
        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            var pathQuery = state.EntityManager.GetComponentData<PathQuerySystem.Singleton>(state.SystemHandle);
            var t = SystemAPI.GetBufferTypeHandle<PathAreaCost>();
            new MyPathJob
            {
                PathQuery = pathQuery,
                AreaCost = new OptionalBufferAccessor<PathAreaCost>(t)
            }.Schedule();
            pathQuery.World.AddDependency(state.Dependency);
        }
        
        partial struct MyPathJob : IJobEntity,IJobEntityChunkBeginEnd
        {
            public PathQuerySystem.Singleton PathQuery;
            public OptionalBufferAccessor<PathAreaCost> AreaCost;
            
            public void Execute([EntityIndexInChunk] int index,ref DynamicBuffer<PathNode> nodes, ref AgentPath path, in AgentBody body, in LocalTransform transform)
            {
                switch (path.State)
                {
                    case AgentPathState.WaitingNewPath:
                        // Release previous handle if it is valid
                        if (PathQuery.Exist(path.QueryHandle))
                            PathQuery.DestroyQuery(path.QueryHandle);

                        path.Location = PathQuery.MapLocation(transform.Position, path.MappingExtent, path.AgentTypeId, path.AreaMask);
                        path.EndLocation = PathQuery.MapLocation(body.Destination, path.MappingExtent, path.AgentTypeId, path.AreaMask);
                        path.QueryHandle = PathQuery.CreateQuery(path.Location, path.EndLocation, path.AgentTypeId, path.AreaMask,
                            AreaCost.TryGetBuffer(index, out var costs) ?
                                costs.Reinterpret<float>().AsNativeArray() : default);
                        path.State = AgentPathState.InProgress;
                        break;

                    case AgentPathState.InProgress:
                        var status = PathQuery.GetStatus(path.QueryHandle);
                        switch (status)
                        {
                            case MQueryStatus.InProgress:
                                break;

                            case MQueryStatus.FinishedFullPath:
                            case MQueryStatus.FinishedPartialPath:    //寻路模块找到路径后，将路径点写入到对应agent的nodebuff中
                                path.State = status == MQueryStatus.FinishedFullPath ? AgentPathState.FinishedFullPath : AgentPathState.FinishedPartialPath;

                                var polygons = PathQuery.GetPolygons(path.QueryHandle);
                                nodes.ResizeUninitialized(polygons.Length);
                                for (int i = 0; i < polygons.Length; i++)
                                {
                                    nodes[i] = new PathNode { Value = polygons[i] };
                                }
                                
                                // Release query so it could be reused
                                PathQuery.DestroyQuery(path.QueryHandle);
                                path.QueryHandle = PathQueryHandle.Null;
                                break;
                            case MQueryStatus.Failed:
                                path.State = AgentPathState.Failed;
                                
                                nodes.ResizeUninitialized(0);
                                
                                // Release query so it could be reused
                                PathQuery.DestroyQuery(path.QueryHandle);
                                path.QueryHandle = PathQueryHandle.Null;
                                break;
                            
                            default:
                                break;
                        }
                        break;
                    case AgentPathState.InValid:
                        if (path.AutoRepath)
                            path.State = AgentPathState.WaitingNewPath;
                        break;
                }
            }
            
            public bool OnChunkBegin(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
            {
                AreaCost.Update(chunk);
                return true;
            }

            public void OnChunkEnd(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask,
                bool chunkWasExecuted)
            {
            }
        }
    }
}